package render

import (
	"gin-sns/app/common"
	"gin-sns/app/common/avatar"
	"gin-sns/app/common/urls"
	"gin-sns/app/ginframe/tools"
	"gin-sns/app/model"
	"html/template"
	"strconv"
	"strings"
	"time"

	"gin-sns/app/model/constants"
	articleModel "gin-sns/app/model/module/article"
	categoryModel "gin-sns/app/model/module/category"
	commentModel "gin-sns/app/model/module/comment"
	favoriteModel "gin-sns/app/model/module/favorite"
	messageModel "gin-sns/app/model/module/message"
	postModel "gin-sns/app/model/module/post"
	rssModel "gin-sns/app/model/module/rss"
	tagModel "gin-sns/app/model/module/tag"
	userModel "gin-sns/app/model/module/user"

	"gin-sns/app/controller/api/cache"
	"gin-sns/app/service/module"

	"github.com/PuerkitoBio/goquery"
	"github.com/tidwall/gjson"
)

func BuildUserDefaultIfNull(id int64) *model.UserInfo {
	user := cache.UserCache.Get(id)
	if user == nil {
		user = &userModel.Entity{}
		user.Id = id
		user.UserName = strconv.FormatInt(id, 10)
		user.Avatar = avatar.GetDefaultAvatar(id)
		user.CreateTime = time.Now()
	}
	return BuildUser(user)
}

func BuildUserById(id int64) *model.UserInfo {
	user := cache.UserCache.Get(id)
	if user == nil {
		return nil
	}
	return BuildUser(user)
}

func BuildUser(user *userModel.Entity) *model.UserInfo {
	if user == nil {
		return nil
	}
	a := user.Avatar
	if len(a) == 0 {
		a = avatar.GetDefaultAvatar(user.Id)
	}

	return &model.UserInfo{
		//Id:          user.Id,
		Username:    user.UserName,
		Nickname:    user.Nickname,
		Avatar:      a,
		Email:       user.UserEmail,
		Type:        user.Type,
		Description: user.Description,
		PasswordSet: len(user.Password) > 0,
		CreateTime:  user.CreateTime.Unix(),
	}
}

func BuildUsers(users []userModel.Entity) []model.UserInfo {
	if len(users) == 0 {
		return nil
	}
	var responses []model.UserInfo
	for _, user := range users {
		item := BuildUser(&user)
		if item != nil {
			responses = append(responses, *item)
		}
	}
	return responses
}

func BuildCategory(category *categoryModel.Entity) *model.CategoryResponse {
	if category == nil {
		return nil
	}
	return &model.CategoryResponse{CategoryId: category.Id, CategoryName: category.Title}
}

func BuildCategories(categories []categoryModel.Entity) []model.CategoryResponse {
	if categories == nil || len(categories) == 0 {
		return nil
	}
	var responses []model.CategoryResponse
	for _, category := range categories {
		responses = append(responses, *BuildCategory(&category))
	}
	return responses
}

func BuildArticle(article *articleModel.Entity) *model.ArticleResponse {
	if article == nil {
		return nil
	}

	rsp := &model.ArticleResponse{}
	rsp.ArticleId = article.Id
	rsp.Title = article.Title
	rsp.Summary = article.Summary
	//rsp.Share = article.Share
	rsp.SourceUrl = article.SourceUrl
	rsp.CreateTime = article.CreateTime.Unix()

	rsp.User = BuildUserDefaultIfNull(article.UserId)

	if article.CategoryId > 0 {
		category := cache.CategoryCache.Get(article.CategoryId)
		rsp.Category = BuildCategory(category)
	}

	tagIds := cache.ArticleTagCache.Get(article.Id)
	tags := cache.TagCache.GetList(tagIds)
	rsp.Tags = BuildTags(tags)

	if article.ContentType == constants.ContentTypeMarkdown {
		mr := tools.NewMd(tools.MdWithTOC()).Run(article.Content)
		rsp.Content = template.HTML(BuildHtmlContent(mr.ContentHtml))
		rsp.Toc = template.HTML(mr.TocHtml)
		if len(rsp.Summary) == 0 {
			rsp.Summary = mr.SummaryText
		}
	} else {
		rsp.Content = template.HTML(BuildHtmlContent(article.Content))
		if len(rsp.Summary) == 0 {
			rsp.Summary = tools.GetSummary(article.Content, 256)
		}
	}

	return rsp
}

func BuildArticles(articles []articleModel.Entity) []model.ArticleResponse {
	if articles == nil || len(articles) == 0 {
		return nil
	}
	var responses []model.ArticleResponse
	for _, article := range articles {
		responses = append(responses, *BuildArticle(&article))
	}
	return responses
}

func BuildSimpleArticle(article *articleModel.Entity) *model.ArticleSimpleResponse {
	if article == nil {
		return nil
	}

	rsp := &model.ArticleSimpleResponse{}
	rsp.ArticleId = article.Id
	rsp.Title = article.Title
	rsp.Summary = article.Summary
	//rsp.Share = article.Share
	rsp.SourceUrl = article.SourceUrl
	rsp.CreateTime = article.CreateTime.Unix()

	rsp.User = BuildUserDefaultIfNull(article.UserId)

	if article.CategoryId > 0 {
		category := cache.CategoryCache.Get(article.CategoryId)
		rsp.Category = BuildCategory(category)
	}

	tagIds := cache.ArticleTagCache.Get(article.Id)
	tags := cache.TagCache.GetList(tagIds)
	rsp.Tags = BuildTags(tags)

	if article.ContentType == constants.ContentTypeMarkdown {
		if len(rsp.Summary) == 0 {
			mr := tools.NewMd(tools.MdWithTOC()).Run(article.Content)
			rsp.Summary = mr.SummaryText
		}
	} else {
		if len(rsp.Summary) == 0 {
			rsp.Summary = tools.GetSummary(tools.GetHtmlText(article.Content), 256)
		}
	}

	return rsp
}

func BuildSimpleArticles(articles []articleModel.Entity) []model.ArticleSimpleResponse {
	if articles == nil || len(articles) == 0 {
		return nil
	}
	var responses []model.ArticleSimpleResponse
	for _, article := range articles {
		responses = append(responses, *BuildSimpleArticle(&article))
	}
	return responses
}

func BuildFeedArticle(article *articleModel.Entity) *model.ArticleFeedResponse {
	if article == nil {
		return nil
	}

	rsp := &model.ArticleFeedResponse{}
	rsp.ArticleId = article.Id
	rsp.Title = article.Title
	rsp.Summary = article.Summary
	//rsp.Share =  1//article.Share
	rsp.SourceUrl = article.SourceUrl
	rsp.CreateTime = article.CreateTime.Unix()

	rsp.User = BuildUserDefaultIfNull(article.UserId)

	//todo
	if article.CategoryId > 0 {
		category := cache.CategoryCache.Get(article.CategoryId)
		rsp.Category = category.Title
	}

	tagIds := cache.ArticleTagCache.Get(article.Id)
	tags := cache.TagCache.GetList(tagIds)
	rsp.Tags = BuildTagsString(tags)

	if article.ContentType == constants.ContentTypeMarkdown {
		if len(rsp.Summary) == 0 {
			mr := tools.NewMd(tools.MdWithTOC()).Run(article.Content)
			rsp.Summary = mr.SummaryText
		}
	} else {
		if len(rsp.Summary) == 0 {
			rsp.Summary = tools.GetSummary(tools.GetHtmlText(article.Content), 256)
		}
	}

	rsp.Content = article.Content

	return rsp
}

func BuildFeedArticles(articles []articleModel.Entity) []model.ArticleFeedResponse {
	if articles == nil || len(articles) == 0 {
		return nil
	}
	var responses []model.ArticleFeedResponse
	for _, article := range articles {
		responses = append(responses, *BuildFeedArticle(&article))
	}
	return responses
}

func BuildPost(post *postModel.Entity) *model.PostResponse {
	if post == nil {
		return nil
	}

	rsp := &model.PostResponse{}

	rsp.PostId = post.Id
	rsp.Title = post.Title
	rsp.User = BuildUserDefaultIfNull(post.UserId)
	rsp.LastCommentTime = post.LastCommentTime.Unix()
	rsp.CreateTime = post.CreateTime.Unix()
	rsp.CountView = post.CountView
	rsp.CountComment = post.CountComment
	rsp.CountLike = post.CountLike

	//tags := postService.GetPostTags(post.Id)
	//rsp.Tags = BuildTags(tags)

	mr := tools.NewMd(tools.MdWithTOC()).Run(post.Content)
	rsp.Content = template.HTML(BuildHtmlContent(mr.ContentHtml))
	rsp.Toc = template.HTML(mr.TocHtml)

	return rsp
}

func BuildPosts(posts []postModel.Entity) []model.PostResponse {
	if posts == nil || len(posts) == 0 {
		return nil
	}
	var responses []model.PostResponse
	for _, post := range posts {
		responses = append(responses, *BuildPost(&post))
	}
	return responses
}

func BuildSimplePost(post *postModel.Entity) *model.PostSimpleResponse {
	if post == nil {
		return nil
	}

	rsp := &model.PostSimpleResponse{}

	rsp.PostId = post.Id
	rsp.Title = post.Title
	rsp.User = BuildUserDefaultIfNull(post.UserId)
	rsp.LastCommentTime = post.LastCommentTime.Unix()
	rsp.CreateTime = post.CreateTime.Unix()
	rsp.CountView = post.CountView
	rsp.CountComment = post.CountComment
	rsp.CountLike = post.CountLike

	//tags := postService.GetPostTags(post.Id)
	//rsp.Tags = BuildTags(tags)
	return rsp
}

func BuildSimplePosts(posts []postModel.Entity) []model.PostSimpleResponse {
	if posts == nil || len(posts) == 0 {
		return nil
	}
	var responses []model.PostSimpleResponse
	for _, post := range posts {
		responses = append(responses, *BuildSimplePost(&post))
	}
	return responses
}

func BuildRss(rss *rssModel.Entity) *model.RssResponse {
	if rss == nil {
		return nil
	}
	rsp := &model.RssResponse{}
	rsp.RssId = rss.Id
	rsp.User = BuildUserDefaultIfNull(rss.UserId)
	rsp.Title = rss.Title
	rsp.Url = rss.Url
	rsp.IconUrl = rss.IconUrl
	rsp.Category = rss.Category
	rsp.CreateTime = rss.CreateTime.Unix()

	return rsp
}

func BuildSimpleRsss(rsss []rssModel.Entity) []model.RssSimpleResponse {
	if rsss == nil || len(rsss) == 0 {
		return nil
	}
	var responses []model.RssSimpleResponse
	for _, rss := range rsss {
		responses = append(responses, *BuildSimpleRss(&rss))
	}
	return responses
}

func BuildSimpleRss(rss *rssModel.Entity) *model.RssSimpleResponse {
	if rss == nil {
		return nil
	}
	rsp := &model.RssSimpleResponse{}
	rsp.RssId = rss.Id
	rsp.User = BuildUserDefaultIfNull(rss.UserId)
	rsp.Title = rss.Title
	rsp.Url = rss.Url
	rsp.IconUrl = rss.IconUrl
	rsp.Category = rss.Category
	rsp.CreateTime = rss.CreateTime.Unix()

	return rsp
}

func BuildComments(comments []commentModel.Entity) []model.CommentResponse {
	var ret []model.CommentResponse
	for _, comment := range comments {
		ret = append(ret, *BuildComment(comment))
	}
	return ret
}

func BuildComment(comment commentModel.Entity) *model.CommentResponse {
	return _buildComment(&comment, true)
}

func _buildComment(comment *commentModel.Entity, buildQuote bool) *model.CommentResponse {
	if comment == nil {
		return nil
	}
	markdownResult := tools.NewMd().Run(comment.Content)
	content := template.HTML(markdownResult.ContentHtml)

	ret := &model.CommentResponse{
		CommentId:  comment.Id,
		User:       BuildUserDefaultIfNull(comment.UserId),
		EntityType: comment.EntityType,
		EntityId:   comment.EntityId,
		Content:    content,
		QuoteId:    comment.QuoteId,
		Status:     comment.Status,
		CreateTime: comment.CreateTime.Unix(),
	}

	if buildQuote && comment.QuoteId > 0 {
		comment, _ := module.CommentService.SelectRecordById(comment.QuoteId)
		quote := _buildComment(comment, false)
		if quote != nil {
			ret.Quote = quote
			ret.QuoteContent = template.HTML(quote.User.Nickname+"：") + quote.Content
		}
	}
	return ret
}

func BuildTag(tag *tagModel.Entity) *model.TagResponse {
	if tag == nil {
		return nil
	}
	return &model.TagResponse{TagId: tag.Id, TagName: tag.Title}
}

func BuildTags(tags []tagModel.Entity) *[]model.TagResponse {
	if len(tags) == 0 {
		return nil
	}
	var responses []model.TagResponse
	for _, tag := range tags {
		responses = append(responses, *BuildTag(&tag))
	}
	return &responses
}

func BuildTagsString(tags []tagModel.Entity) string {
	if len(tags) == 0 {
		return ""
	}
	var responses string
	for _, tag := range tags {
		responses += tag.Title
	}
	return responses
}

func BuildFavorite(favorite *favoriteModel.Entity) *model.FavoriteResponse {
	rsp := &model.FavoriteResponse{}
	rsp.FavoriteId = favorite.Id
	rsp.EntityType = favorite.EntityType
	rsp.CreateTime = favorite.CreateTime.Unix()

	if favorite.EntityType == constants.EntityTypeArticle {
		article, _ := module.ArticleService.SelectRecordById(favorite.EntityId)
		if article == nil || article.Status != model.ArticleStatusPublished {
			rsp.Deleted = true
		} else {
			rsp.Url = urls.ArticleUrl(article.Id)
			rsp.User = BuildUserById(article.UserId)
			rsp.Title = article.Title
			if article.ContentType == constants.ContentTypeMarkdown {
				rsp.Content = common.GetMarkdownSummary(article.Content)
			} else {
				doc, err := goquery.NewDocumentFromReader(strings.NewReader(article.Content))
				if err == nil {
					text := doc.Text()
					rsp.Content = tools.GetSummary(text, 256)
				}
			}
		}
	} else {
		post, _ := module.PostService.SelectRecordById(favorite.EntityId)
		if post == nil || post.Status != model.PostStatusOk {
			rsp.Deleted = true
		} else {
			rsp.Url = urls.PostUrl(post.Id)
			rsp.User = BuildUserById(post.UserId)
			rsp.Title = post.Title
			rsp.Content = common.GetMarkdownSummary(post.Content)
		}
	}
	return rsp
}

func BuildFavorites(favorites []favoriteModel.Entity) []model.FavoriteResponse {
	if favorites == nil || len(favorites) == 0 {
		return nil
	}
	var responses []model.FavoriteResponse
	for _, favorite := range favorites {
		responses = append(responses, *BuildFavorite(&favorite))
	}
	return responses
}

func BuildMessage(message *messageModel.Entity) *model.MessageResponse {
	if message == nil {
		return nil
	}

	detailUrl := ""
	if message.Type == model.MsgTypeComment {
		entityType := gjson.Get(message.ExtraData, "entityType").Int()
		entityId := gjson.Get(message.ExtraData, "entityId").Int()
		if entityType == constants.EntityTypeArticle {
			detailUrl = urls.ArticleUrl(entityId)
		} else if entityType == constants.EntityTypePost {
			detailUrl = urls.PostUrl(entityId)
		}
	}
	from := BuildUserDefaultIfNull(message.FromId)
	if message.FromId <= 0 {
		from.Nickname = "系统通知"
		from.Avatar = avatar.DefaultAvatars[0]
	}
	return &model.MessageResponse{
		MessageId:    message.Id,
		From:         from,
		UserId:       message.UserId,
		Content:      message.Content,
		QuoteContent: message.QuoteContent,
		//Type:         message.Type,
		DetailUrl:  detailUrl,
		ExtraData:  message.ExtraData,
		Status:     message.Status,
		CreateTime: message.CreateTime.Unix(),
	}
}

func BuildMessages(messages []messageModel.Entity) []model.MessageResponse {
	if len(messages) == 0 {
		return nil
	}
	var responses []model.MessageResponse
	for _, message := range messages {
		responses = append(responses, *BuildMessage(&message))
	}
	return responses
}

func BuildHtmlContent(htmlContent string) string {
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(htmlContent))
	if err != nil {
		return htmlContent
	}

	doc.Find("a").Each(func(i int, selection *goquery.Selection) {
		href := selection.AttrOr("href", "")

		if len(href) == 0 {
			return
		}

		// 不是内部链接
		if !urls.IsInternalUrl(href) {
			selection.SetAttr("target", "_blank")
			selection.SetAttr("rel", "external nofollow") // 标记站外链接，搜索引擎爬虫不传递权重值

			newHref := tools.ParseUrl(urls.AbsUrl("/redirect")).AddQuery("url", href).BuildStr()
			selection.SetAttr("href", newHref)

		}

		// 如果是锚链接
		if urls.IsAnchor(href) {
			selection.ReplaceWithHtml(selection.Text())
		}

		// 如果a标签没有title，那么设置title
		title := selection.AttrOr("title", "")
		if len(title) == 0 {
			selection.SetAttr("title", selection.Text())
		}
	})

	// 处理图片
	doc.Find("img").Each(func(i int, selection *goquery.Selection) {
		src := selection.AttrOr("src", "")
		if strings.Contains(src, "qpic.cn") {
			newSrc := tools.ParseUrl("/api/img/proxy").AddQuery("url", src).BuildStr()
			selection.SetAttr("src", newSrc)
		}
	})

	html, err := doc.Html()
	if err != nil {
		return htmlContent
	}
	return html
}
